use std::hash::{Hash, Hasher};
use std::{
    collections::{BinaryHeap, HashMap},
    fmt::{self, Display},
};

use rand;
use rand_distr::{Distribution, Exp, Normal, Uniform};

#[derive(Debug, Clone)]
pub enum Delay {
    Instant,
    Normal(f64, f64),
    Exponatial(f64),
    Uniform(f64, f64),
}

impl Delay {
    pub fn sample(&self, rng: &mut rand::rngs::ThreadRng) -> f64 {
        match self {
            Delay::Instant => 0.0,
            Delay::Normal(mean, std) => {
                let normal_dist = Normal::new(*mean, *std).unwrap();
                normal_dist.sample(rng)
            }
            Delay::Exponatial(mean) => {
                let exp_dist = Exp::new(1.0 / *mean).unwrap();
                exp_dist.sample(rng)
            }
            Delay::Uniform(min, max) => {
                let uniform_dist = Uniform::new_inclusive(*min, *max);
                uniform_dist.sample(rng)
            }
        }
    }

    pub fn to_string(&self) -> String {
        match self {
            Delay::Instant => "Instant".to_string(),
            Delay::Normal(mean, std) => format!("Normal({},{})", mean, std),
            Delay::Exponatial(mean) => format!("Exponatial({})", mean),
            Delay::Uniform(min, max) => format!("Uniform({},{})", min, max),
        }
    }
}

impl Display for Delay {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

#[cfg(test)]
mod test_delay {
    use crate::model::cptem::Delay;

    #[test]
    fn test_sample() {
        let mut rng = rand::thread_rng();
        let delay = Delay::Normal(10.0, 2.0);
        let d = delay.sample(&mut rng);
        println!("正态分布生成的时间: {:?}", d);

        let delay = Delay::Exponatial(5.0);
        let d = delay.sample(&mut rng);
        println!("指数分布生成的时间: {:?}", d);

        let delay = Delay::Uniform(1.0, 10.0);
        let d = delay.sample(&mut rng);
        println!("均匀分布生成的时间: {:?}", d);

        let delay = Delay::Instant;
        let d = delay.sample(&mut rng);
        println!("直接生成的时间: {:?}", d);
    }

    #[test]
    fn test_to_string() {
        let delay = Delay::Normal(10.0, 2.0);
        let d = delay.to_string();
        println!("分布: {:?}", d);

        let delay = Delay::Exponatial(5.0);
        let d = delay.to_string();
        println!("分布: {:?}", d);

        let delay = Delay::Uniform(1.0, 10.0);
        let d = delay.to_string();
        println!("分布: {:?}", d);

        let delay = Delay::Instant;
        let d = delay.to_string();
        println!("瞬态: {:?}", d);
    }
}

#[derive(Debug, Eq, Clone)]
pub struct Action {
    pub name: Option<String>,
    pub entity: Option<String>,
}

impl Action {
    pub fn new(name: Option<String>, entity: Option<String>) -> Action {
        Action { name, entity }
    }

    pub fn to_string(&self) -> String {
        let name = match &self.name {
            Some(name) => name.to_string(),
            None => "None".to_string(),
        };
        let entity = match &self.entity {
            Some(entity) => entity.to_string(),
            None => "None".to_string(),
        };
        format!("Action({},{})", name, entity)
    }
}

impl PartialEq for Action {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.entity == other.entity
    }
}

impl PartialEq<str> for Action {
    fn eq(&self, other: &str) -> bool {
        self.to_string() == other
    }
}

impl PartialEq<&str> for Action {
    fn eq(&self, other: &&str) -> bool {
        self.to_string() == *other
    }
}

#[cfg(test)]
mod test_action {
    use crate::model::cptem::Action;

    #[test]
    fn test_new() {
        let action = Action::new(Some("name".to_string()), Some("entity".to_string()));
        println!("action: {:?}", action);
    }

    #[test]
    fn test_to_string() {
        let action = Action::new(Some("name".to_string()), Some("entity".to_string()));
        println!("action: {}", action.to_string());
    }

    #[test]
    fn test_eq() {
        let action = Action::new(Some("name".to_string()), Some("entity".to_string()));
        let action2 = Action::new(Some("name".to_string()), Some("entity".to_string()));
        assert_eq!(action, action2);

        let s = "Action(name,entity)";
        print!("{}", action == s);
        assert_eq!(action, s)
    }
}

#[derive(Debug, Clone)]
pub struct EvolutionCondition {
    pub time: f64,
    pub action: Action,
}

impl Eq for EvolutionCondition {}

impl PartialEq for EvolutionCondition {
    fn eq(&self, other: &Self) -> bool {
        ((self.time - other.time).abs() < f64::EPSILON) && self.action == other.action
    }
}

impl Ord for EvolutionCondition {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.time
            .partial_cmp(&other.time)
            .unwrap_or(std::cmp::Ordering::Equal)
    }
}

impl PartialOrd for EvolutionCondition {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Display for EvolutionCondition {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl EvolutionCondition {
    pub fn new(time: f64, action: Action) -> EvolutionCondition {
        EvolutionCondition { time, action }
    }

    pub fn to_string(&self) -> String {
        format!(
            "EvolutionCondition({}, {})",
            self.time,
            self.action.to_string()
        )
    }
}

#[derive(Debug)]
pub struct EvolutionConditionQueue {
    pub queue: BinaryHeap<EvolutionCondition>,
}

impl EvolutionConditionQueue {
    pub fn new() -> EvolutionConditionQueue {
        EvolutionConditionQueue {
            queue: BinaryHeap::new(),
        }
    }

    pub fn push(&mut self, time: f64, action: Action) {
        let ec = EvolutionCondition { time, action };
        self.queue.push(ec);
    }

    pub fn pop(&mut self) -> Option<EvolutionCondition> {
        self.queue.pop()
    }

    pub fn peek(&self) -> Option<&EvolutionCondition> {
        self.queue.peek()
    }

    pub fn del(&mut self, time: f64, action_name: &str, entity_name: &str) {
        for _ in 0..self.queue.len() {
            let ec = self.queue.peek().unwrap();
            if ec.time == time
                && ec.action.name == Some(action_name.to_string())
                && ec.action.entity == Some(entity_name.to_string())
            {
                self.queue.pop();
            }
        }
    }

    pub fn len(&self) -> usize {
        self.queue.len()
    }

    pub fn is_empty(&self) -> bool {
        self.queue.is_empty()
    }

    pub fn to_string(&self) -> String {
        let evolution_conditions = self.queue.clone().into_sorted_vec();
        evolution_conditions
            .iter()
            .map(|ec| ec.to_string())
            .collect::<Vec<String>>()
            .join(",\n ")
    }
}

#[cfg(test)]
mod test_evolution_condition_queue {
    use crate::model::cptem::Action;
    use crate::model::cptem::EvolutionConditionQueue;

    #[test]
    fn test_new() {
        let queue = EvolutionConditionQueue::new();
        println!("queue: {:?}", queue);
    }

    #[test]
    fn test_push() {
        let mut queue = EvolutionConditionQueue::new();
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        queue.push(
            2.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        queue.push(
            0.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        queue.push(
            0.8,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        println!("queue: {}", queue.to_string());
    }

    #[test]
    fn test_pop() {
        let mut queue = EvolutionConditionQueue::new();
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        let ec = queue.pop();
        println!("ec: {:?}", ec);
    }

    #[test]
    fn test_peek() {
        let mut queue = EvolutionConditionQueue::new();
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        let ec = queue.peek();
        println!("ec: {:?}", ec);
    }

    #[test]
    fn test_del() {
        let mut queue = EvolutionConditionQueue::new();
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        let ec = queue.pop();
        queue.del(1.0, "name", "entity");
        println!("ec: {:?}", ec);
    }

    #[test]
    fn test_len() {
        let mut queue = EvolutionConditionQueue::new();
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        let len = queue.len();
        println!("len: {}", len);
    }

    #[test]
    fn test_is_empty() {
        let mut queue = EvolutionConditionQueue::new();
        queue.push(
            1.0,
            Action::new(Some("name".to_string()), Some("entity".to_string())),
        );
        let is_empty = queue.is_empty();
        println!("is_empty: {}", is_empty);
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct State {
    pub name: String,
    pub is_initial: bool,
}

impl State {
    pub fn new(name: String, is_initial: bool) -> State {
        State { name, is_initial }
    }

    pub fn to_string(&self) -> String {
        format!("State({},{})", self.name, self.is_initial)
    }

    // fn hash(&self) -> u64 {
    //     let mut hasher = std::collections::hash_map::DefaultHasher::new();
    //     self.name.hash(&mut hasher);
    //     hasher.finish()
    // }
}

impl Display for State {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

#[cfg(test)]
mod test_state {

    use crate::model::cptem::State;

    #[test]
    fn test_new() {
        let state = State::new("name".to_string(), true);
        println!("state: {:?}", state);
    }

    #[test]
    fn test_to_string() {
        let state = State::new("name".to_string(), true);
        println!("state: {}", state.to_string());
    }

    #[test]
    fn test_eq() {
        let state1 = State::new("s1".to_string(), true);
        let state2 = State::new("s1".to_string(), false);
        let state3 = state1.clone();
        println!("{}", state1 == state2);
        println!("{}", state1 == state3);
    }
}

#[derive(Debug, Clone)]
pub enum ValueOfAttribute {
    String(String),
    Number(f64),
    Paths(Vec<Vec<String>>),
}

// #[derive(Debug)]
#[derive(Clone)]
pub struct Guard<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool,
{
    pub func: F,
}

impl<F> fmt::Debug for Guard<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Guard").field("func", &"closure").finish()
    }
}

impl<F> Guard<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool,
{
    pub fn new(func: F) -> Self {
        Guard { func }
    }

    pub fn call(&self, action: &Action, attributes: &HashMap<String, ValueOfAttribute>) -> bool {
        (self.func)(action, attributes)
    }
}

#[cfg(test)]
mod test_guard {
    use crate::model::cptem::Action;
    use crate::model::cptem::Guard;
    use crate::model::cptem::ValueOfAttribute;
    use std::collections::HashMap;

    #[test]
    fn test_new() {
        let func =
            |action: &Action, attributes: &HashMap<String, ValueOfAttribute>| -> bool { true };
        let guard = Guard::new(func);
        println!("guard: {:?}", guard);
    }

    #[test]
    fn test_call() {
        let func = |action: &Action, attributes: &HashMap<String, ValueOfAttribute>| -> bool {
            action == "Action(name,entity)"
        };
        let guard = Guard::new(func);
        let action = Action::new(Some("name".to_string()), Some("entity".to_string()));
        let attributes = HashMap::new();
        let result = guard.call(&action, &attributes);
        println!("result: {}", result);
    }
}

#[derive(Clone)]
pub struct Inhibition<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool,
{
    pub func: F,
}

impl<F> fmt::Debug for Inhibition<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Inhibition")
            .field("func", &"closure")
            .finish()
    }
}

impl<F> Inhibition<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool,
{
    pub fn new(func: F) -> Self {
        Inhibition { func }
    }

    pub fn call(&self, action: &Action, attributes: &HashMap<String, ValueOfAttribute>) -> bool {
        (self.func)(action, attributes)
    }
}

#[cfg(test)]
mod test_inhibition {
    use crate::model::cptem::Action;
    use crate::model::cptem::Inhibition;
    use crate::model::cptem::ValueOfAttribute;
    use std::collections::HashMap;

    #[test]
    fn test_new() {
        let func =
            |action: &Action, attributes: &HashMap<String, ValueOfAttribute>| -> bool { true };
        let inhibition = Inhibition::new(func);
        println!("inhibition: {:?}", inhibition);
    }

    #[test]
    fn test_call() {
        let func = |action: &Action, attributes: &HashMap<String, ValueOfAttribute>| -> bool {
            action == "Action(name,entity)"
        };
        let inhibition = Inhibition::new(func);
        let action = Action::new(Some("name".to_string()), Some("entity".to_string()));
        let attributes = HashMap::new();
        let result = inhibition.call(&action, &attributes);
        println!("result: {}", result);
    }
}

#[derive(Clone)]
pub struct Update<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> HashMap<String, ValueOfAttribute>,
{
    pub func: F,
}

impl<F> fmt::Debug for Update<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> HashMap<String, ValueOfAttribute>,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Update").field("func", &"closure").finish()
    }
}

impl<F> Update<F>
where
    F: Fn(&Action, &HashMap<String, ValueOfAttribute>) -> HashMap<String, ValueOfAttribute>,
{
    pub fn new(func: F) -> Self {
        Update { func }
    }

    pub fn call(
        &self,
        action: &Action,
        attributes: &HashMap<String, ValueOfAttribute>,
    ) -> HashMap<String, ValueOfAttribute> {
        (self.func)(action, attributes)
    }
}

#[cfg(test)]
mod test_update {
    use crate::model::cptem::Action;
    use crate::model::cptem::Update;
    use crate::model::cptem::ValueOfAttribute;
    use std::collections::HashMap;

    #[test]
    fn test_new() {
        let func = |action: &Action,
                    attributes: &HashMap<String, ValueOfAttribute>|
         -> HashMap<String, ValueOfAttribute> {
            HashMap::from([
                ("data".to_string(), ValueOfAttribute::Number(10.0)),
                (
                    "active".to_string(),
                    ValueOfAttribute::String("sdf".to_string()),
                ),
            ])
        };
        let update = Update::new(func);
        println!("update: {:?}", update);
    }

    #[test]
    fn test_call() {
        let attributes = HashMap::from([("data".to_string(), ValueOfAttribute::Number(10.0))]);
        let func = |action: &Action,
                    attributes: &HashMap<String, ValueOfAttribute>|
         -> HashMap<String, ValueOfAttribute> {
            HashMap::from([
                ("data".to_string(), attributes["data"].clone()),
                (
                    "active".to_string(),
                    ValueOfAttribute::String("sdf".to_string()),
                ),
            ])
        };
        let update = Update::new(func);
        let action = Action::new(Some("name".to_string()), Some("entity".to_string()));
        let result = update.call(&action, &attributes);
        println!("result: {:?}", result);
    }
}

#[derive(Debug, Clone)]
pub struct Transition {
    pub name: String,
    pub source: State,
    pub target: State,
    pub delay: Delay,
    pub probability: Option<f64>,
    pub action: Option<Action>,
    pub guard: Option<Guard<fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool>>,
    pub inhibition: Option<Inhibition<fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool>>,
    pub update: Option<
        Update<
            fn(&Action, &HashMap<String, ValueOfAttribute>) -> HashMap<String, ValueOfAttribute>,
        >,
    >,
}

impl Hash for Transition {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
    }
}

impl Eq for Transition {}

impl PartialEq for Transition {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
    }
}

impl Ord for Transition {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.name.cmp(&other.name)
    }
}

impl PartialOrd for Transition {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Transition {
    pub fn new(
        name: String,
        source: State,
        target: State,
        delay: Delay,
        probability: Option<f64>,
        action: Option<Action>,
        guard: Option<Guard<fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool>>,
        inhibition: Option<Inhibition<fn(&Action, &HashMap<String, ValueOfAttribute>) -> bool>>,
        update: Option<
            Update<
                fn(
                    &Action,
                    &HashMap<String, ValueOfAttribute>,
                ) -> HashMap<String, ValueOfAttribute>,
            >,
        >,
    ) -> Transition {
        let mut transition = Transition {
            name,
            source,
            target,
            delay,
            probability: None,
            action: None,
            guard: None,
            inhibition: None,
            update: None,
        };
        if let Some(probability) = probability {
            transition.probability = Some(probability)
        }
        if let Some(action) = action {
            transition.action = Some(action)
        }
        if let Some(guard) = guard {
            transition.guard = Some(guard)
        }
        if let Some(inhibition) = inhibition {
            transition.inhibition = Some(inhibition)
        }
        if let Some(update) = update {
            transition.update = Some(update)
        }
        return transition;
    }

    pub fn to_string(&self) -> String {
        format!("Transition(name:{:?}, source:{:?}, target:{:?}, delay:{:?}, probability:{:?}, action:{:?}, guard:{:?}, inhibition:{:?}, update:{:?})", 
            self.name, self.source, self.target, self.delay, self.probability, self.action, self.guard, self.inhibition, self.update)
    }

    pub fn get_source(&self) -> &State {
        &self.source
    }

    pub fn get_target(&self) -> &State {
        &self.target
    }
}

#[cfg(test)]
mod test_transition {
    use crate::model::cptem::Action;
    use crate::model::cptem::Delay;
    use crate::model::cptem::Guard;
    use crate::model::cptem::Inhibition;
    use crate::model::cptem::State;
    use crate::model::cptem::Transition;
    use crate::model::cptem::Update;
    use crate::model::cptem::ValueOfAttribute;
    use std::collections::HashMap;
    use std::rc::Rc;

    fn setup() -> Transition {
        let guard = Guard::new(|action: &Action,
                                attributes: &HashMap<String, ValueOfAttribute>|
         -> bool { true } as _);
        let inhibition = Inhibition::new(|action: &Action,
                                          attributes: &HashMap<String, ValueOfAttribute>|
         -> bool { true } as _);
        let update = Update::new(|action: &Action,
                                  attributes: &HashMap<String, ValueOfAttribute>|
         -> HashMap<String, ValueOfAttribute> {
            HashMap::from([(
                "data".to_string(),
                ValueOfAttribute::String("dfsd".to_string()),
            )])
        } as _);

        let source = State::new("source".to_string(), false);
        let target = State::new("target".to_string(), false);

        let transition = Transition::new(
            "name".to_string(),
            source,
            target,
            Delay::Instant,
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            Some(guard),
            Some(inhibition),
            Some(update),
        );
        transition
    }

    #[test]
    fn test_new() {
        let transition = setup();
        println!("transition: {:?}", transition);
    }

    #[test]
    fn test_to_string() {
        let transition = setup();
        print!("transition: {}", transition.to_string());
    }

    #[test]
    fn test_get_source_and_target() {
        let transition = setup();
        let source = transition.get_source();
        let target = transition.get_target();
        println!("source: {}", source.to_string());
        println!("target: {}", target.to_string());
    }
}

#[derive(Debug, Clone)]
pub struct EntityTranstion {
    pub time: f64,
    pub entity: String,
    pub transition: Transition,
}

impl Eq for EntityTranstion {}

impl PartialEq for EntityTranstion {
    fn eq(&self, other: &Self) -> bool {
        self.time == other.time
    }
}

impl Ord for EntityTranstion {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.time.partial_cmp(&other.time).unwrap()
    }
}

impl PartialOrd for EntityTranstion {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(&other))
    }
}

impl EntityTranstion {
    pub fn new(time: f64, entity: String, transition: Transition) -> EntityTranstion {
        EntityTranstion {
            time,
            entity,
            transition,
        }
    }

    pub fn to_string(&self) -> String {
        format!(
            "EntityTranstion(time:{:?}, entity:{:?}, transition:{:?})",
            self.time, self.entity, self.transition
        )
    }
}

#[cfg(test)]
mod test_entity_transition {
    use crate::model::cptem::*;

    fn setup() -> EntityTranstion {
        let source = State::new("source".to_string(), true);
        let target = State::new("source".to_string(), false);
        let transition = Transition::new(
            "name".to_string(),
            source,
            target,
            Delay::Instant,
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        EntityTranstion::new(1.0, "entity".to_string(), transition)
    }

    #[test]
    fn test_new() {
        let entity_transition = setup();
        println!("entity_transition: {:?}", entity_transition);
    }

    #[test]
    fn test_to_string() {
        let entity_transition = setup();
        println!("entity_transition: {}", entity_transition.to_string());
    }
}

#[derive(Debug)]
pub struct TransitionQueue {
    pub queue: BinaryHeap<EntityTranstion>,
}

impl TransitionQueue {
    pub fn new() -> TransitionQueue {
        TransitionQueue {
            queue: BinaryHeap::new(),
        }
    }

    pub fn push(&mut self, time: f64, entity: String, transition: Transition) {
        let entity_transition = EntityTranstion {
            time,
            entity,
            transition,
        };
        self.queue.push(entity_transition);
    }

    pub fn pop(&mut self) -> Option<EntityTranstion> {
        self.queue.pop()
    }

    pub fn peek(&self) -> Option<&EntityTranstion> {
        self.queue.peek()
    }

    pub fn del(&mut self, time: f64, entity: &str, transition_name: &str) {
        for _ in 0..self.queue.len() {
            let ec = self.queue.peek().unwrap();
            if ec.time == time && ec.entity == entity && ec.transition.name == transition_name {
                self.queue.pop();
            }
        }
    }

    pub fn is_empty(&self) -> bool {
        self.queue.is_empty()
    }

    pub fn len(&self) -> usize {
        self.queue.len()
    }

    pub fn to_string(&self) -> String {
        let transitions = self.queue.clone().into_sorted_vec();
        transitions
            .iter()
            .map(|transition| transition.to_string())
            .collect::<Vec<String>>()
            .join(", \n")
    }
}

#[cfg(test)]
mod test_transition_queue {
    use crate::model::cptem::*;

    fn setup() -> TransitionQueue {
        let source = State::new("source".to_string(), true);
        let target = State::new("source".to_string(), false);
        let transition = Transition::new(
            "name".to_string(),
            source,
            target,
            Delay::Instant,
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        let mut transition_queue = TransitionQueue::new();
        transition_queue.push(1.0, "entity".to_string(), transition);
        transition_queue
    }

    #[test]
    fn test_new() {
        let transition_queue = setup();
        println!("{:?}", transition_queue);
    }

    #[test]
    fn test_to_string() {
        let transition_queue = setup();
        print!("transition_queue: {}", transition_queue.to_string())
    }
}

#[derive(Debug, Clone)]
pub struct ComponentProbabilityTimedEvolutionModel {
    pub entity: String,
    pub states: HashMap<State, HashMap<State, Vec<Transition>>>,
    pub current_state: Option<State>,
    pub firing_transition: Option<Transition>,
    pub attr: Option<HashMap<String, ValueOfAttribute>>,
}

impl ComponentProbabilityTimedEvolutionModel {
    pub fn new(entity: String) -> ComponentProbabilityTimedEvolutionModel {
        ComponentProbabilityTimedEvolutionModel {
            entity,
            states: HashMap::new(),
            current_state: None,
            firing_transition: None,
            attr: None,
        }
    }

    pub fn add_transition(&mut self, transition: Transition) {
        self.states
            .entry(transition.source.clone())
            .or_insert_with(HashMap::new)
            .entry(transition.target.clone())
            .or_insert_with(Vec::new)
            .push(transition);
    }

    pub fn find_state_by_name(&self, name: &str) -> Option<&State> {
        for (state, _) in &self.states {
            if state.name == name {
                return Some(state);
            }
        }
        None
    }

    pub fn initial_current_state(&mut self) {
        for (state, _) in &self.states {
            if state.is_initial {
                self.current_state = Some(state.clone());
                break;
            }
        }
    }

    pub fn set_current_state(&mut self, name: &str) {
        if let Some((state, _)) = self
            .states
            .iter_mut()
            .find(|(state, _)| state.name == *name)
        {
            self.current_state = Some(state.clone())
        } else {
            eprintln!("State {} not found", name)
        }
    }

    pub fn get_current_state(&self) -> Option<State> {
        self.current_state.clone()
    }

    pub fn states(&self) -> Vec<&State> {
        self.states.keys().collect()
    }

    pub fn set_attr(&mut self, attr: HashMap<String, ValueOfAttribute>) {
        self.attr = Some(attr);
    }

    pub fn get_attr(&self) -> Option<HashMap<String, ValueOfAttribute>> {
        self.attr.clone()
    }

    pub fn transitions(&self) -> Vec<&Transition> {
        self.states
            .values()
            .flat_map(|state_map: &HashMap<State, Vec<Transition>>| state_map.values())
            .flatten()
            .collect()
    }

    pub fn next_state(&self) -> Option<Vec<&State>> {
        if self.get_current_state().is_none() {
            return None;
        }

        if self.firing_transition.is_some() {
            return None;
        }

        let mut next_states = Vec::<&State>::new();
        let current_state = self.get_current_state().unwrap();

        for transition in self.transitions() {
            if transition.source.name == current_state.name {
                next_states.push(&transition.target);
            }
        }
        Some(next_states)
    }

    pub fn find_enable_transitions(&self, action: Option<Action>) -> Vec<&Transition> {
        if self.current_state.is_none() {
            return Vec::new();
        }

        if self.firing_transition.is_some() {
            return Vec::new();
        }

        let current_state = self.current_state.as_ref().unwrap();
        let out_transitions: Vec<&Transition> = self
            .states
            .get(current_state)
            .unwrap()
            .values()
            .flatten()
            .collect();

        let mut enabled_transitions = Vec::new();
        for transition in out_transitions {
            if let Some(guard) = &transition.guard {
                if let Some(ref act) = action {
                    let result = guard.call(act, self.attr.as_ref().unwrap());
                    if result {
                        enabled_transitions.push(transition);
                    }
                }
            } else {
                enabled_transitions.push(transition);
            }
        }
        enabled_transitions
    }

    pub fn firing_transition(&mut self, transition: Transition) {
        if self.firing_transition.is_some() {
            eprintln!("Firing transition already exists");
        } else {
            self.firing_transition = Some(transition);
        }
    }

    pub fn inhibit_transition(&mut self, transition: Transition) {
        if self.firing_transition.is_none() {
            eprintln!("No firing transition");
        } else {
            self.firing_transition = None;
        }
    }

    pub fn fired_transition(&mut self, action: Action) {
        if self.firing_transition.is_none() {
            eprintln!("No firing transition");
        } else {
            let firing_transition = self.firing_transition.as_ref().unwrap().clone();
            if let Some(current_state) = self.get_current_state() {
                if current_state.name != firing_transition.source.name {
                    eprintln!("Firing transition source does not match current state");
                } else {
                    let target_state_name = firing_transition.target.name.clone();
                    self.set_current_state(&target_state_name);
                    self.firing_transition = None;
                    if let Some(update) = &firing_transition.update {
                        update.call(&action, self.attr.as_mut().unwrap());
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod test_component_probability_timed_evolution_model {
    use crate::model::cptem::*;

    fn setup() -> ComponentProbabilityTimedEvolutionModel {
        let s1 = State::new("work".to_string(), true);
        let s2 = State::new("fail".to_string(), false);
        let t1 = Transition::new(
            "fail".to_string(),
            s1.clone(),
            s2.clone(),
            Delay::Exponatial(1.0),
            Some(1.0),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        let t2 = Transition::new(
            "repair".to_string(),
            s2.clone(),
            s1.clone(),
            Delay::Exponatial(10.0),
            Some(1.0),
            Some(Action::new(
                Some("repair".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        let mut cptem = ComponentProbabilityTimedEvolutionModel::new("n1".to_string());
        cptem.add_transition(t1);
        cptem.add_transition(t2);
        cptem.initial_current_state();
        cptem
    }

    #[test]
    fn test_new() {
        let cptem = setup();
        println!("{:?}", cptem);
    }

    // #[test]
    // fn test_to_string() {
    //     let cptem = setup();
    //     print!("cptem: {}", cptem.to_string());
    // }

    #[test]
    fn test_next_state() {
        let cptem = setup();
        println!("{:?}", cptem.next_state());
    }

    #[test]
    fn test_find_enable_transitions() {
        let mut cptem = setup();
        let s1 = cptem.find_state_by_name("work").unwrap();

        let t3 = Transition::new(
            "maintain".to_string(),
            s1.clone(),
            s1.clone(),
            Delay::Exponatial(1.0),
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        cptem.add_transition(t3);
        let result: Vec<&Transition> = cptem.find_enable_transitions(None);
        println!("{:?}", result);
        // println!("{:?}", cptem.find_enable_transitions(None));
    }

    #[test]
    fn test_firing_transition() {
        let mut cptem = setup();
        let source = State::new("source".to_string(), true);
        let target = State::new("source".to_string(), false);
        let transition = Transition::new(
            "name".to_string(),
            source,
            target,
            Delay::Instant,
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        cptem.firing_transition(transition);
        println!("{:?}", cptem.firing_transition);
    }

    #[test]
    fn test_inhibit_transition() {
        let mut cptem = setup();
        let source = State::new("source".to_string(), true);
        let target = State::new("source".to_string(), false);
        let transition = Transition::new(
            "name".to_string(),
            source,
            target,
            Delay::Instant,
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        cptem.firing_transition(transition.clone());
        cptem.inhibit_transition(transition.clone());
        println!("{:?}", cptem.firing_transition);
    }

    #[test]
    fn test_fired_transition() {
        let mut cptem = setup();
        let source = State::new("source".to_string(), true);
        let target = State::new("source".to_string(), false);
        let transition = Transition::new(
            "name".to_string(),
            source,
            target,
            Delay::Instant,
            Some(0.7),
            Some(Action::new(
                Some("fail".to_string()),
                Some("n1".to_string()),
            )),
            None,
            None,
            None,
        );
        cptem.fired_transition(Action::new(
            Some("fail".to_string()),
            Some("n1".to_string()),
        ));
        println!("{:?}", cptem.firing_transition);
    }

    #[test]
    fn test_input() {
        use std::io;
        let mut s = String::new();
        let _ = io::stdin().read_line(&mut s);
        let words: Vec<_> = s.split(" ").collect();
        println!("s: {}", s);
        println!("{:?}", words);
    }
}
